package com.ds.infrastructure.hyperspace.container.flow.handler.security;

import com.ds.infrastructure.hyperspace.common.Container;
import com.ds.infrastructure.hyperspace.common.flow.FlowHandlerPipeline;
import com.ds.infrastructure.hyperspace.container.flow.context.HyperspaceDuplexFlowContext;
import com.ds.infrastructure.hyperspace.container.flow.handler.AbstractPreFlowHandler;
import com.ds.infrastructure.hyperspace.container.flow.operation.EntryFlowOperation;
import com.ds.infrastructure.hyperspace.container.internal.operator.OperatorDefinition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static com.ds.infrastructure.hyperspace.common.StateEnum.REQUEST_BLOCKED;
import static com.ds.infrastructure.hyperspace.container.flow.handler.security.ListTypeEnum.BLACK;
import static com.ds.infrastructure.hyperspace.container.flow.handler.security.ListTypeEnum.WHITE;
import static com.ds.infrastructure.hyperspace.container.internal.util.StringConstant.EMPTY_STR;

/**
 * usage eg:
 *  -   handler: blackOrWhiteListPreHandler
 *      handlerParams: [black|white, value1|value2|valueN, uri|ip|headerName, (operatorName,operatorArgs)...]
 * @author  Weishaoying
 */
@Slf4j
public class BlackOrWhiteListPreFlowHandler extends AbstractPreFlowHandler {

    private static final int INPUT_NAME_INDEX = 2;

    private volatile ListTypeEnum listType;

    private Map<String, String> valueMap = new ConcurrentHashMap<>();

    private volatile String inputName;

    private volatile List<OperatorDefinition> operatorDefinitionList;

    @Override
    public void handleInput(HyperspaceDuplexFlowContext duplexFlowContext, Object data) {
        if (data == duplexFlowContext.getRequestStartState()) {
            if (inputName == null) {
                return;
            }
            String inputValue = extractAndComputeInput(duplexFlowContext, inputName, operatorDefinitionList);
            if(StringUtils.isEmpty(inputValue)){
                return;
            }
            String matchValue = valueMap.get(inputValue);
            EntryFlowOperation entryFlowOperation = duplexFlowContext.getEntryFlowOperation();
            if (listType == BLACK && matchValue != null) {
                entryFlowOperation.interruptAndCloseContext(duplexFlowContext, REQUEST_BLOCKED,
                        "Hit black list, match type " + inputName + ", match value " + matchValue);
            } else if (listType == WHITE && matchValue == null) {
                entryFlowOperation.interruptAndCloseContext(duplexFlowContext, REQUEST_BLOCKED,
                        "Hit white list, match type " + inputName + ", match value " + inputValue);
            }
        }
    }

    @Override
    public String handlerName() {
        return "blackOrWhiteListPreHandler";
    }

    @Override
    public void initHandlerParameters(Container container, FlowHandlerPipeline pipeline, String[] handlerParams) {
        if (handlerParams.length <= INPUT_NAME_INDEX) {
            return;
        }
        String tempListType = handlerParams[0];
        if (BLACK.getTypeValue().equals(tempListType)) {
            listType = BLACK;
        } else if (WHITE.getTypeValue().equals(tempListType)) {
            listType = WHITE;
        } else {
            return;
        }

        String[] valueArray = StringUtils.delimitedListToStringArray(
                handlerParams[1], OR_DELIMITER);
        if (valueArray.length > 0) {
            for (String detectValue : valueArray) {
                valueMap.putIfAbsent(detectValue, EMPTY_STR);
            }
        }
        inputName = handlerParams[INPUT_NAME_INDEX];
        operatorDefinitionList = parseOperatorDefinition(container, handlerParams, INPUT_NAME_INDEX + 1);
    }

}
